﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Text;
using System.Threading.Tasks;
using XT.Model;
using System.Linq.Expressions;

namespace XT.Repository
{
    public abstract class Repository<U, V> : IRepository<U, V> where U : class, IEntity<V>
    {
        protected DataContext Context { get; set; }

        public bool Compare<V>(V x, V y) where V : class, IEntity
        {
            return x == y;
        }

        public virtual U Find(V id)
        {
            return ExecuteQuery(context => context.GetTable<U>().SingleOrDefault(c => c.Id.Equals(id)));
            //return Context.GetTable<U>().Where(c => EqualityComparer<V>.Default.Equals(c.Id,id)).SingleOrDefault();
        }
        public virtual T ExecuteQuery<T>(Func<DataContext, T> pred)
        {
            return ExecuteQuery(pred, Context);
        }

        public virtual T ExecuteQuery<T>(Func<DataContext, T> pred, DataContext ctx)
        {
            return pred.Invoke(ctx);
        }

        public virtual IEnumerable<T> FindAll<T>(OrderByDelegate<T> orderBy) where T : class, IEntity
        {
            return orderBy(Context.GetTable<T>());
        }

        public virtual IEnumerable<T> FindAll<T>(OrderByDelegate<T> orderBy, int pageIndex, int pageSize) where T : class, IEntity
        {
            return orderBy(Context.GetTable<T>().Skip((pageIndex - 1) * pageSize).Take(pageSize));
        }

        public virtual IEnumerable<T> FindAllByCriteria<T>(Expression<Func<T, bool>> exp, OrderByDelegate<T> orderBy) where T : class, IEntity
        {
            return orderBy(Context.GetTable<T>().Where(exp));
        }

        public virtual IEnumerable<T> FindAllByCriteria<T>(Expression<Func<T, bool>> exp, OrderByDelegate<T> orderBy, int pageIndex, int pageSize) where T : class, IEntity
        {
            return FindAllByCriteria(exp,orderBy).Skip((pageIndex - 1) * pageSize).Take(pageSize);
        }

        public virtual Account checkLogin(string mail, string password)
        {
            return ExecuteQuery(context => context.GetTable<Account>().SingleOrDefault(c => c.Account_Email.Equals(mail) && c.Account_Password.Equals(password)));
        }

        public virtual void Add(U u) 
        {
            Context.GetTable<U>().InsertOnSubmit(u);
            Context.SubmitChanges();
        }

        public virtual U Save(V id, Func<DataContext, U, bool> pred)
        {
            if (pred == null)
            {
                throw new ArgumentNullException();
            }

            U saving = Find(id);

            if (pred(Context, saving))
            {
                Context.SubmitChanges();
            }
            else
            {
                throw new System.Data.DataException();
            }

            Context.Refresh(RefreshMode.OverwriteCurrentValues, Find(id));

            return saving;
        }
    }
}
